#ifndef _MODBUS_H_
#define _MODBUS_H_
#include <pthread.h>

#include <QDialog>

#include <QtGui>
#include <QtNetwork>


enum MODBUS_STATUS {
  MODBUS_OK=0, MODBUS_TIMEOUT=-1, 
  MODBUS_ERRO_COMUNICACAO=-2, MODBUS_ERRO_DADO=-3,
  MODBUS_RESPOSTA_ERRO=-4
};

class Modbus
{
protected:
  // Dados
  int sockId;
  // Funcoes que nao devem ser chamadas nunca...
  Modbus(const Modbus&);
  void operator=(const Modbus&);
  // Funcoes protegidas
  virtual ssize_t virtRecv(void *buf, size_t len, int flags)=0;
  // Timeout em milissegundos
  ssize_t myRecv(int timeout, void *buf, size_t len, int flags);
  void limpaSocket(void);
  MODBUS_STATUS leIdPoco(int timeout, uint8_t &idPoco);
  MODBUS_STATUS leFuncao(int timeout, uint8_t &funcao);

  MODBUS_STATUS escreveIdFuncao(uint8_t idPoco, uint8_t funcao);
  MODBUS_STATUS escreveDados(const uint8_t *dados, int16_t lenDados);
  Modbus();
  virtual ~Modbus();
};

class ModbusMestre: public Modbus
{
private:
  pthread_mutex_t mutex;
  // Funcoes privadas
  ssize_t virtRecv(void *buf, size_t len, int flags);
  MODBUS_STATUS leTamanho(int timeout, int16_t &lenDados);
  MODBUS_STATUS leDados(int timeout, uint8_t *dados, int16_t &lenDados);
  MODBUS_STATUS escreveRegistros(uint16_t registro, uint16_t numReg);
public:
  int requestReadAndWait(int to, uint8_t *data_,uint8_t wellId, uint16_t registerId, uint16_t &dataLen, int numberOdRegisters);
  ModbusMestre(const char *nome, unsigned porta);
  MODBUS_STATUS write_request(uint8_t idPoco,
			      uint16_t registro, uint16_t numReg,
			      const uint8_t *dados);
  MODBUS_STATUS request_data(uint8_t idPoco,
			     uint16_t registro, uint16_t numReg);
  MODBUS_STATUS receive_data(int &timeout, uint8_t &idPoco,
			     int16_t &lenDados, uint8_t *&dados);
};

class ModbusEscravo: public Modbus
{
private:
  // Dados
  bool conectado;
  uint8_t modBuffer[20000];
  // Funcoes privadas
  ssize_t virtRecv(void *buf, size_t len, int flags);
  MODBUS_STATUS criaSocket(unsigned porta);
  MODBUS_STATUS leRegistroNormalizado(int timeout, uint16_t &regNorm);
  MODBUS_STATUS leNumeroRegistros(int timeout, uint16_t &numReg);
  MODBUS_STATUS leDadosBuffer(int timeout, int16_t lenDados);
  MODBUS_STATUS escreveTamanho(int16_t lenDados);
public:
  ModbusEscravo(unsigned porta);
  inline bool estaConectado() {return conectado;}
  MODBUS_STATUS aceitarNovaConexao(unsigned porta);
  MODBUS_STATUS read_request(int timeout, uint8_t &idPoco, bool &leitura,
			     uint16_t &registro, uint16_t &numReg);
  inline const uint8_t *buffer() {return modBuffer;}
  MODBUS_STATUS write_data(uint8_t idPoco, uint16_t registro, uint16_t numReg,
			   const uint8_t *dados);
};

#endif
